#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

#include <net/if.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define UDP_PORT 50001
#define TCP_PORT 50002

struct file_info
{
    char name[32];
    int size;
};

int main(int argc, char const *argv[])
{
    int udpfd = socket(AF_INET, SOCK_DGRAM, 0);  // 收发文件的基本信息
    int tcpfd = socket(AF_INET, SOCK_STREAM, 0); // 收发文件的具体内容

    // 准备好UDP/TCP
    struct sockaddr_in addr, peeraddr;
    socklen_t len = sizeof(addr);
    bzero(&addr, len);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(UDP_PORT);

    bind(udpfd, (struct sockaddr *)&addr, len);

    addr.sin_port = htons(TCP_PORT);
    bind(tcpfd, (struct sockaddr *)&addr, len);
    listen(tcpfd, 3);

    // 使用UDP等待对方要发来的文件的基本信息
    struct file_info info;
    len = sizeof(peeraddr);
    bzero(&info, sizeof(info));
    bzero(&peeraddr, len);
    recvfrom(udpfd, &info, sizeof(info), 0, (struct sockaddr *)&peeraddr, &len);
    printf("有人要给你发文件：%s[%d], 是否接收？[y/n]", info.name, info.size);

    char response = getchar();
    int connfd;
    char *buf;
    FILE *fp;
    if(response == 'y')
    {
        // 通知对方赶紧把文件发来
        sendto(udpfd, &response, 1, 0, (struct sockaddr *)&peeraddr, len);

        // 准备TCP接收对方文件
        connfd = accept(tcpfd, NULL, NULL);
        if(connfd == -1)
        {
            perror("连接失败:");
        }

        int bufsize = info.size > 1024 ? 1024 : info.size;
        buf = calloc(1, bufsize);

        fp = fopen(info.name, "w");

        int total = 0;
        while(1)
        {
            int n = read(connfd, buf, bufsize);

            if(n == -1)
            {
                perror("读取TCP数据失败");
                break;
            }
            if(n == 0)
                break;

            int m = fwrite(buf, 1, n, fp);
            total += m;

            printf("已接收%d个字节\n", total);
        }
    }
    else
    {
        // 通知对方取消发送
        sendto(udpfd, &response, 1, 0, (struct sockaddr *)&peeraddr, len);
        exit(0);
    }

    // 关闭、释放所有资源
    close(udpfd);
    close(tcpfd);
    close(connfd);
    free(buf);
    fclose(fp);

    return 0;
}
